Adding Interactivity to the
Elegance of ggplot2 with ggiraph

Static plots tell a story
Interactive plots invites people to explore the story

“If you know ggplot2…you already know ggiraph”
-Plausible quote from Hadley Wickham

ggiraph interactive geomsTotal of 50 interactive geoms! Consistent naming convention to match ggplot2 geoms
| ggplot2 | ggiraph | |
|---|---|---|
geom_point |
➡️ | geom_point_interactive |
geom_text |
➡️ | geom_text_interactive |
geom_line |
➡️ | geom_line_interactive |
geom_tile |
➡️ | geom_tile_interactive |
✅ Familiar syntax - Just add _interactive to your geoms
✅ No JavaScript required - Stay in your R comfort zone
✅ R universe- Works with Quarto, Shiny, R Markdown,
✅ HTML widgets - Easy to embed and share
✅ Use with other extensions - Doesn’t interefere with non-interactive geoms
p_simpsons <-
p_simpsons_base +
geom_tile_interactive(
aes(tooltip = title, data_id = id),
color = "white", stroke = .2
) +
geomtextpath::geom_texthline(
yintercept = 9.5, linewidth = 1, label = "Season 10 starts",
vjust = 1.4, hjust = .995, family = "Rethink Sans", lineheight = .6
)
girafe(
ggobj = p_simpsons,
width_svg = 10.8, height_svg = 9.5,
options = list(
opts_tooltip(
opacity = 1, use_fill = TRUE,
css = "color: black; padding: 15px;"
),
opts_sizing(width = .7),
opts_hover(css = "stroke-width: 2;"),
opts_hover_inv(css = "opacity: 0.3;")
)
)p_simpsons_base <-
simpsons_imdb |>
mutate(
title_wrapped = stringr::str_replace_all(stringr::str_wrap(title, 22), "\\n", "<br>"),
text_color = if_else(rating > 6.3 & rating < 8.5, "black", "white"),
lab = paste0("<span style='font-family:rethink sans;color:", text_color, ";'>", "S",
sprintf("%02d", season), " E", sprintf("%02d", episode),
"<br><b style='font-size:150%;font-weight:600;font-family:piazzolla;'>",
title_wrapped, "</b><br><br>IMDb Rating: ", sprintf("%1.1f", rating))
)
p_simpsons_advanced <-
p_simpsons_base +
geom_tile_interactive(aes(tooltip = lab, data_id = id), color = "white", stroke = .2) +
geomtextpath::geom_texthline(
yintercept = 9.5, linewidth = 1, label = "Season 10 starts",
vjust = 1.4, hjust = .995, family = "Rethink Sans", lineheight = .6
)Embed Ced’s bike plot here
doctor_who_basic_plot<-ggplot() +
#interactive points per episode
ggiraph::geom_jitter_interactive(
data = df_eps,
position = position_jitter(seed = 42, height = .2, width =3),
mapping = aes(
data_id = story_number,
x = rating,
y = reorder(doctor, avg_rating),
fill = I(color),
tooltip = tooltip
),
shape = 21,
color = "black",
size = 3,
alpha = 0.8
) +
geomtextpath::geom_textvline(
mapping = aes(
xintercept = overall_avg,
label = paste0("Overall Avg: ", round(overall_avg, 0))
),
size = 3,
color = pal_line,
hjust = 0.86,
vjust = -.2,
family = "Roboto"
) +
geom_segment(
data = df_doc_avg,
mapping = aes(
x = avg_rating,
xend = overall_avg,
y = doctor,
yend = doctor
),
color = pal_line
) +
geom_point(
data = df_doc_avg,
mapping = aes(x = avg_rating, y = doctor, fill = I(color)),
shape = 21,
color = "white",
size = 10
) +
geom_image(
data = df_doc_avg,
mapping = aes(x = avg_rating, y = doctor, image = image),
size = 0.06,
asp = 1.61
) +
geom_text(
data = df_doc_avg,
mapping = aes(
x = avg_rating,
y = doctor,
label = round(avg_rating, 1)
),
size = 2.5,
fontface= "bold",
color = "white",
vjust = 3.75,
family = "Roboto"
) +
geom_textbox(
data = df_doc_avg,
mapping = aes(x = 59.1, y = doctor, label = label),
family = "Roboto",
fill = NA,
box.size = NA,
box.padding = unit(rep(0, 4), "pt"),
color = pal_text,
hjust = 0
) +
#arrows
annotate(
geom = "text",
label = "Avg Rating\nper Doctor",
x = 76,
y = 2.5,
size = 2.5,
color = "white",
family = "Roboto"
) +
geom_curve(
mapping = aes(
x = 77,
xend = 81.4,
y = 2.7,
yend = 3
),
color = "white",
curvature = -0.2,
linewidth = 0.3,
arrow = arrow(length = unit(0.08, "in"))
) +
geom_curve(
mapping = aes(
x = 77,
xend = 80.8,
y = 2.3,
yend = 2
),
color = "white",
curvature = 0.2,
linewidth = 0.3,
arrow = arrow(length = unit(0.08, "in"))
) +
scale_x_continuous(
limits = c(59, 95),
expand = c(0, 0),
breaks = c(70, 75, 80, 85, 90, 95)
) +
coord_equal(ratio = 50 / 12) +
labs(
title = "Doctor Who was The Best?",
subtitle = "Ratings by Episode and Doctor for the popular TV series, Doctor Who.",
x = "Rating"
)+
theme(
legend.position = "none",
plot.background = element_rect(fill = pal_bg, color = pal_bg),
panel.background = element_blank(),
panel.grid = element_blank(),
plot.margin = margin(
l = 20,
r = 40,
b = 10,
t = 20
),
plot.caption = element_text(size = 7, color = "grey80"),
plot.title = element_text(
size = 14,
face = "bold",
margin = margin(b = 5)
),
plot.subtitle = element_text(size = 9, color = "#BABABA"),
text = element_text(color = pal_text, family = "Roboto"),
axis.text = element_text(color = pal_text, family = "Roboto Mono"),
axis.text.y = element_blank(),
axis.title.y = element_blank(),
axis.title.x = element_textbox_simple(
margin = margin(t = 10),
halign = 0.675,
hjust = 0.5
),
axis.ticks = element_blank()
)
ggiraph::girafe(
ggobj = doctor_who_basic_plot,
options = list(
ggiraph::opts_toolbar(saveaspng = F),
ggiraph::opts_tooltip(css = "font-family:Roboto;"),
#modify hover css
ggiraph::opts_hover(css = "fill:white;stroke:grey;cursor:help;")
)
)ggiraph::girafe(
ggobj = doctor_who_advanced_plot,
width_svg = 6.125, height_svg = 4.5,
options = list(
#turnoff download png
ggiraph::opts_toolbar(saveaspng = F),
ggiraph::opts_sizing(width = .8),
#default tooltip font
ggiraph::opts_tooltip(
css = "font-family:Roboto;"
),
#remove default opts_hover settings
ggiraph::opts_hover(css=""),
#inverted hover, color points grey
ggiraph::opts_hover_inv(
girafe_css(
css = "",
point = "fill:#515151",
text = NULL
)
)
)
)combined_owid <- plot_owid + map_owid +
plot_layout(ncol = 2, widths = c(.4, .6)) +
plot_annotation(theme = theme(plot.margin = margin(12, 12, 12, 12)))
girafe(
ggobj = combined_owid, width_svg = 12, height_svg = 5.3,
options = list(
opts_tooltip(use_fill = TRUE, css = "
font-size: 17px;
font-weight: 400;
font-family: Spline Sans;
color:white;
padding: 10px;
border:2px solid white;
border-radius: 5px;
"),
opts_hover(css = "stroke: white; stroke-width: 0.5px; opacity: 1;"),
opts_hover_inv(css = "opacity: 0.2;"),
opts_toolbar(position = "bottomright"),
opts_zoom(min = 1, max = 4)
)
)